package com.econage.eccm.test.base.webdatasource;

import com.econage.eccm.test.base.webdatasource.datasourcebuilder.DataSourceType;
import com.econage.eccm.test.base.webdatasource.datasourcebuilder.TomcatPoolBuilder;
import com.econage.eccm.test.base.webdatasource.facade.ConnectionFacade;
import com.econage.eccm.test.base.webdatasource.facade.StatementFacade;
import com.econage.eccm.test.base.webdatasource.web.AppDataSourceContext;

import javax.sql.DataSource;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Properties;

public class DataSourceConfiguration {

    private Constructor<?> connectionProxyConstructor;
    private Constructor<?> statementProxyConstructor;
    private Constructor<?> preparedStatementProxyConstructor;
    private Constructor<?> callableStatementProxyConstructor;

    private DataSourceType dataSourceType = DataSourceType.TomcatPool; //连接池组件类型，默认TomcatPool，将来考虑c3po，druid等等

    private boolean traceConnectionCreation;//true，跟踪连接创建时的线程栈
    private boolean traceStatementCreation;//true，跟踪语句创建时的线程栈
    private boolean traceResultSetCreation;//true，跟踪结果集创建时的线程栈

    private boolean statementLeakWaring = true;//true,关闭连接时发现有语句未关闭，给出警告
    private boolean resultSetLeakWaring = true;//true,关闭语句时发现有结果集未关闭，给出警告

    private boolean autoCommitFalseForce    = true;//强制修改自动提交操作为false

    private boolean commitBeforeClose       = true;//连接关闭前，提交事务，此时强制更改autoCommit为false
    private boolean ignoreCommitChange      = true;//忽略setAutoCommit操作
    private boolean ignoreTransactionChange = true;//忽略事务调整操作

    private long executeSlowThreshold = 5000;//执行缓慢阈值，毫秒数

    public DataSourceConfiguration() throws NoSuchMethodException {
        initProxyConstructor();
    }

    private void initProxyConstructor() throws NoSuchMethodException {
        if (connectionProxyConstructor == null ) {
            /*Class<?> proxyClass = xa ?
                    Proxy.getProxyClass(DataSourceConfiguration.class.getClassLoader(), new Class[] {java.sql.Connection.class,javax.sql.XAConnection.class}) :
                    Proxy.getProxyClass(DataSourceConfiguration.class.getClassLoader(), new Class[] {java.sql.Connection.class});*/
            connectionProxyConstructor = Proxy.getProxyClass(
                    DataSourceConfiguration.class.getClassLoader(),
                    new Class[] {java.sql.Connection.class}
            ).getConstructor(new Class[] { InvocationHandler.class });
        }
        if(statementProxyConstructor==null){
            statementProxyConstructor = Proxy.getProxyClass(
                    DataSourceConfiguration.class.getClassLoader(),
                    new Class[] {java.sql.Statement.class}
            ).getConstructor(new Class[] { InvocationHandler.class });
        }
        if(preparedStatementProxyConstructor ==null){
            preparedStatementProxyConstructor = Proxy.getProxyClass(
                    DataSourceConfiguration.class.getClassLoader(),
                    new Class[] {java.sql.PreparedStatement.class}
            ).getConstructor(new Class[] { InvocationHandler.class });
        }
        if(callableStatementProxyConstructor ==null){
            callableStatementProxyConstructor = Proxy.getProxyClass(
                    DataSourceConfiguration.class.getClassLoader(),
                    new Class[] {java.sql.CallableStatement.class}
            ).getConstructor(new Class[] { InvocationHandler.class });
        }
    }


    public DataSource buildUnderlyingDataSource(Properties properties) throws SQLException {
        if(dataSourceType==DataSourceType.TomcatPool){
            return new TomcatPoolBuilder()
                    .autoCommitFalseForce(autoCommitFalseForce)
                    .build(properties);
        }else{
            throw new SQLException("unknown dataSourceType!");
        }
    }

    public Connection newConnectionFacade(Connection connection,AppDataSourceContext appDataSourceContext) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        Thread ownerThread = Thread.currentThread();
        ConnectionFacade connectionFacade = new ConnectionFacade(connection,ownerThread,this,appDataSourceContext);
        return (Connection)connectionProxyConstructor.newInstance(connectionFacade);
    }

    public Statement newStatementFacade(Statement statement,Connection proxyConnection) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        StatementFacade statementFacade = new StatementFacade(statement,proxyConnection,null,this);
        return (Statement)statementProxyConstructor.newInstance(statementFacade);
    }

    public PreparedStatement newPreparedStatementFacade(PreparedStatement statement,Connection proxyConnection,String sql) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        StatementFacade statementFacade = new StatementFacade(statement,proxyConnection,sql,this);
        return (PreparedStatement) preparedStatementProxyConstructor.newInstance(statementFacade);
    }

    public CallableStatement newCallableStatementFacade(CallableStatement statement, Connection proxyConnection, String sql) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        StatementFacade statementFacade = new StatementFacade(statement,proxyConnection,sql,this);
        return (CallableStatement) callableStatementProxyConstructor.newInstance(statementFacade);
    }

    public boolean isTraceConnectionCreation() {
        return traceConnectionCreation;
    }

    public void setTraceConnectionCreation(boolean traceConnectionCreation) {
        this.traceConnectionCreation = traceConnectionCreation;
    }

    public boolean isTraceStatementCreation() {
        return traceStatementCreation;
    }

    public void setTraceStatementCreation(boolean traceStatementCreation) {
        this.traceStatementCreation = traceStatementCreation;
    }

    public boolean isTraceResultSetCreation() {
        return traceResultSetCreation;
    }

    public void setTraceResultSetCreation(boolean traceResultSetCreation) {
        this.traceResultSetCreation = traceResultSetCreation;
    }

    public boolean isStatementLeakWaring() {
        return statementLeakWaring;
    }

    public void setStatementLeakWaring(boolean statementLeakWaring) {
        this.statementLeakWaring = statementLeakWaring;
    }

    public boolean isResultSetLeakWaring() {
        return resultSetLeakWaring;
    }

    public void setResultSetLeakWaring(boolean resultSetLeakWaring) {
        this.resultSetLeakWaring = resultSetLeakWaring;
    }

    public boolean isCommitBeforeClose() {
        return commitBeforeClose;
    }

    public void setCommitBeforeClose(boolean commitBeforeClose) {
        this.commitBeforeClose = commitBeforeClose;
    }

    public boolean isIgnoreCommitChange() {
        return ignoreCommitChange;
    }

    public void setIgnoreCommitChange(boolean ignoreCommitChange) {
        this.ignoreCommitChange = ignoreCommitChange;
    }

    public DataSourceType getDataSourceType() {
        return dataSourceType;
    }

    public void setDataSourceType(DataSourceType dataSourceType) {
        this.dataSourceType = dataSourceType;
    }

    public boolean isIgnoreTransactionChange() {
        return ignoreTransactionChange;
    }

    public void setIgnoreTransactionChange(boolean ignoreTransactionChange) {
        this.ignoreTransactionChange = ignoreTransactionChange;
    }

    public boolean isAutoCommitFalseForce() {
        return autoCommitFalseForce;
    }

    public void setAutoCommitFalseForce(boolean autoCommitFalseForce) {
        this.autoCommitFalseForce = autoCommitFalseForce;
    }

    public long getExecuteSlowThreshold() {
        return executeSlowThreshold;
    }

    public void setExecuteSlowThreshold(long executeSlowThreshold) {
        this.executeSlowThreshold = executeSlowThreshold;
    }
}
